package no.ntnu.fp.su;

import no.ntnu.fp.db.Database;
import no.ntnu.fp.su.wellsim.Well;

/**
 * Brønn slik watch ser den, holder alle verdiene for brønnen
 * 
 * @author stn
 * 
 */
public class WatchWell extends Thread {

	// public static int wellID = 1;

	private Log log;
	/** {@link Controlsystem}et, hentes fra platform */
	private ControlSystem cs;
	/** {@link Platform}en hvor denne brønnen hører hjemme */
	private Platform platform;

	private boolean hasCs;
	private boolean statusChange;
	private boolean isRunning;

	private int wellID;
	/** Wellsim brønnen til denne brønnen */
	private Well well;

	/** ErosionProbes */
	private double r;
	/** Temperature */
	private double t;
	/** Pressure */
	private double p;

	/** ErosionProbes previous value */
	private double rPrev;
	/** Temperature previous value */
	private double tPrev;
	/** Pressure previous value */
	private double pPrev;

	/** Change in ErosionProbes */
	private double ev;
	/** Change in Temperature */
	private double dt;
	/** Change in Pressure */
	private double dp;
	/** Change in Change in Pressure */
	private double ddp;

	/** Temperature Limit */
	private double tLim;
	/** Temperature Change Limit */
	private double deltaTlim;
	/** Pressure Limit */
	private double pLim;
	/** Pressure Change Limit */
	private double deltaPlim1;
	/** Change in Pressure Change Limit */
	private double deltaPlim2;
	/** Change in ErosionProbe Limit */
	private double evLim;

	/** Critical value Pressure */
	private double critP;
	/** Critical value R */
	private double critR;

	/** Status of Well */
	private Status status;
	private Database db;

	/**
	 * Constructor for WatchWell
	 * 
	 * @param {@link Platform} - hvilken plattform denne brønnen er på.
	 */
	public WatchWell(Platform platform, int welllid) {
		// setName("" + wellID++);

		db = new Database();
		wellID = 0;
		System.out.println("WatchWell = " + this.getName());
		well = new Well();
		System.out.println("Wellsim " + well.toString() + " created");
		this.platform = platform;
		log = new Log(this.db, wellID);
		System.out.println("Log " + log.toString() + " added");
		cs = this.platform.getControlSystem();

		status = Status.GREEN;

		hasCs = false;
		statusChange = false;

		r = 0;
		t = 0;
		p = 0;
		rPrev = 0;
		tPrev = 0;
		pPrev = 0;
		ev = 0;
		dt = 0;
		dp = 0;
		ddp = 0;
		tLim = 0;
		deltaTlim = 0;
		pLim = 0;
		deltaPlim1 = 0;
		deltaPlim2 = 0;
		evLim = 0;

	}

	/**
	 * Gir {@link Database} som er på denne brønn
	 * 
	 * @return {@link Database} - denne brønnens database
	 */
	public Database getDb() {
		return db;
	}

	/**
	 * Constructor for WatchWell
	 * 
	 * @param {@link Platform} - Platform denne brønnen er på
	 * @param wellId
	 *            - Unik Id for denne brønnen
	 * @param critR
	 *            - Kritisk verdi for R
	 * @param critP
	 *            - Kritisk verdi for Trykk
	 * @param critT
	 *            - Kritisk verdi for Temperatur
	 * @param evLimit
	 *            - Maks tillatt verdi for EV
	 * @param dTlimit
	 *            - Maks tillatt endring i Temperatur
	 * @param dPlimit1
	 *            - Maks tillatt endring i Trykk
	 * @param dPlimit2
	 *            - Maks tillatt endring i endringen i Trykk
	 * @param pLimit
	 *            - Maks tillatt Trykk
	 */
	public WatchWell(Platform platform, int wellId, double critR, double critP,
			double critT, double evLimit, double dTlimit, double dPlimit1,
			double dPlimit2, double pLimit) {

		db = new Database();
		wellID = wellId;
		setName("WatchWell " + wellID);
		System.out.println("WatchWell " + wellID + " = " + this.getName());
		well = new Well();
		// System.out.println("Wellsim " + well.toString() + " created");
		this.platform = platform;
		log = new Log(this.db, wellID);
		// System.out.println("Log " + log.toString() + " added");
		cs = this.platform.getControlSystem();

		status = Status.GREEN;

		hasCs = false;
		statusChange = false;
		isRunning = true;

		r = 0;
		t = 0;
		p = 0;
		rPrev = 0;
		tPrev = 0;
		pPrev = 0;
		ev = 0;
		dt = 0;
		dp = 0;
		ddp = 0;

		tLim = critT;
		deltaTlim = dTlimit;
		pLim = critP;
		deltaPlim1 = dPlimit1;
		deltaPlim2 = dPlimit2;
		evLim = evLimit;

		this.critR = critR;
		this.critP = critP;

	}

	/**
	 * Retunerer Id til denne brønnen
	 * 
	 * @return int - the ID of this well
	 */
	public int getWellID() {
		return wellID;
	}

	/**
	 * Tråd for {@link WatchWell}, interruptes av {@link ControlSystem} når ny
	 * data har kommet. <br>
	 * Vil interrupte {@link Platform}, dersom status har endret seg.
	 */
	public void run() {
		isRunning = true;
		System.out.println(this.getName() + " started");
		if (platform.isWatch()) {
			log.readLogFromDb(1);
		}
		System.out.println("entries: " + getLog().getLog(Filter.ALL).size());
		Status prevStatus = status;
		int alarm = 0;
		while (isRunning) {
			try {
				// System.out.println(this.getName() + " sleeping");
				sleep(500);
			} catch (InterruptedException e) {
				// System.out.println("debug");
				synchronized (this) {
					if ((ev > evLim && (dt < deltaTlim || -dp > deltaPlim1))
							|| ddp > deltaPlim2 || p > pLim || p > critP
							|| t > tLim || r > critR) {

						// if (ev > evLim && dt < deltaTlim)
						// System.out.println("dt < deltaTLim");
						// if (ev > evLim && dp > deltaPlim1)
						// System.out.println("dp > deltaPlim1");
						// if (ddp > deltaPlim2)
						// System.out.println("ddp > deltaPlim2");
						// if (p > pLim)
						// System.out.println("p > pLim");
						if (alarm < 20)
							alarm++;
						if (alarm < 10) {
							if (status != Status.YELLOW) {
								status = Status.YELLOW;
								statusChange = true;
								platform.interrupt();
							}
						} else {
							if (status != Status.RED) {
								status = Status.RED;
								statusChange = true;
								platform.interrupt();
							}
						}
					} else {

						if (alarm > -10)
							alarm -= 2;
						else if (status != Status.GREEN) {
							status = Status.GREEN;
							statusChange = true;
							platform.interrupt();
						}
					}
					// System.out.println("Adding to log: " + wellID + " " +
					// status
					// + " " + r + " " + ev + " " + t + " " + p);
					log.addEntry(new LogEntry(this.wellID, status, r, ev, t, p,
							null));
					if (status != prevStatus && status == Status.RED) {
						platform.getEvents().addEvent(
								new Event(platform.getEvents().newId(), null,
										"Status Red in well " + wellID));
					}
					prevStatus = status;

					// System.out.println(status);
				}
			}
		}
	}

	/**
	 * Stopper tråden, brønnen fjernes helt, også fra {@link ControlSystem}
	 */
	public synchronized void stopRunning() {
		System.out.println("stopping " + this.getName());
		cs.removeWell(this);
		do {
			isRunning = false;
		} while (isRunning);
	}

	/**
	 * Starts polling data from wellsim
	 */
	public void startPoll() {
		if (!hasCs) {
			System.out.println("Added " + this.getName());
			if (platform.isWatch()) {
				cs.addWell(this);
			} else {
				log.readLogFromDb(60);
				cs.addDBpoller(this);
			}
			hasCs = true;
		}
	}

	/**
	 * Stops polling data from wellsim
	 */
	public void stopPoll() {
		if (hasCs) {
			cs.removeWell(this);
			hasCs = false;
		}
	}

	/**
	 * Sender en Alarm
	 * 
	 */
	public void raiseAlarm() {

	}

	/**
	 * Merker en rød status som falsk Alarm
	 */
	public void supressAlarm() {

	}

	/**
	 * Updates the well data, called from the polling method
	 * 
	 * @param sampledata
	 *            - double array of size 4, containing e1, e2, p and t in that
	 *            order
	 */
	public synchronized void updateData(double[] sampledata) {
		if (sampledata.length != 4)
			return;

		rPrev = r;
		pPrev = p;
		tPrev = t;

		r = (sampledata[0] + sampledata[1]) / 2;
		p = sampledata[2];
		t = sampledata[3];

		ev = r - rPrev;
		dt = t - tPrev;
		ddp = p - pPrev - dp;
		dp = p - pPrev;

		// System.out.println(this.getName() + "  " + r + " " + p + " " + t);
	}

	/**
	 * Gets the wellsim attached to this object
	 * 
	 * @return {@link Well} - Well simulator attached to this {@link WatchWell}
	 */
	public Well getWell() {
		return well;
	}

	/**
	 * Returns the {@link Log} attached to this object
	 * 
	 * @return {@link Log}
	 */
	public Log getLog() {
		return log;
	}

	/**
	 * Returns whether status has been changed on this well
	 * 
	 * @return status changed since last check
	 */
	public synchronized boolean getStatusChanged() {
		// boolean tmp = statusChange;
		// statusChange = false;
		return statusChange;
	}

	/**
	 * Clears the statusChanged flag
	 */
	public synchronized void clearStatusChanged() {
		statusChange = false;
	}

	/**
	 * Gets the Status of this well
	 * 
	 * @return status of well
	 */
	public Status getStatus() {
		return status;
	}

	/**
	 * Gets the current R
	 * 
	 * @return double R
	 */
	public double getR() {
		return r;
	}

	/**
	 * Gets the current Temperature
	 * 
	 * @return double T
	 */
	public double getT() {
		return t;
	}

	/**
	 * Gets the current Pressure
	 * 
	 * @return double P
	 */
	public double getP() {
		return p;
	}

	/**
	 * Gets the current value of EV
	 * 
	 * @return double EV
	 */
	public double getEv() {
		return ev;
	}

	/**
	 * Gets the current change in Temperature
	 * 
	 * @return double dT
	 */
	public double getDt() {
		return dt;
	}

	/**
	 * Gets the current change in Pressure
	 * 
	 * @return double dP
	 */
	public double getDp() {
		return dp;
	}

	/**
	 * Gets the current change in the change of Pressure
	 * 
	 * @return double ddP
	 */
	public double getDdp() {
		return ddp;
	}

	/**
	 * Gets the value of {@link deltaTlim}
	 * 
	 * @return double deltaTlim
	 */
	public double getDeltaTlim() {
		return deltaTlim;
	}

	/**
	 * Sets the value of {@link deltaTlim}, will write the new value to the
	 * {@link Database}.
	 * 
	 * @param double - new deltaTlim
	 */
	public void setDeltaTlim(double deltaTlim) {
		if (getDeltaTlim() == deltaTlim) {
			return;
		}
		db.connect();
		String q = "UPDATE welldata SET dtlimit = " + deltaTlim
				+ " WHERE wellid = " + this.wellID;
		db.execute(q);
		db.disconnect();
		this.deltaTlim = deltaTlim;
	}

	/**
	 * Gets the value of {@link pLim}
	 * 
	 * @return double pLim
	 */
	public double getPLim() {
		return pLim;
	}

	/**
	 * Sets the value of {@link pLim}, will write the new value to the
	 * {@link Database}.
	 * 
	 * @param double - new pLim
	 */
	public void setPLim(double plim) {
		if (getPLim() == plim) {
			return;
		}
		db.connect();
		String q = "UPDATE welldata SET dplimit = " + plim + " WHERE wellid = "
				+ this.wellID;
		db.execute(q);
		db.disconnect();
		this.pLim = plim;
	}

	/**
	 * 
	 * @return
	 */
	public double getDeltaPlim1() {
		return deltaPlim1;
	}

	/**
	 * Sets the value of {@link deltaPlim1}, will write the new value to the
	 * {@link Database}.
	 * 
	 * @param double - new deltaPlim1
	 */
	public void setDeltaPlim1(double deltaPlim1) {
		if (getDeltaPlim1() == deltaPlim1) {
			return;
		}
		db.connect();
		String q = "UPDATE welldata SET dplimit1 = " + deltaPlim1
				+ " WHERE wellid = " + this.wellID;
		db.execute(q);
		db.disconnect();
		this.deltaPlim1 = deltaPlim1;
	}

	public double getDeltaPlim2() {
		return deltaPlim2;
	}

	/**
	 * Sets the value of {@link deltaPlim2}, will write the new value to the
	 * {@link Database}.
	 * 
	 * @param double - new deltaPlim2
	 */
	public void setDeltaPlim2(double deltaPlim2) {
		if (getDeltaPlim2() == deltaPlim2) {
			return;
		}
		String q = "UPDATE welldata SET dplimit2 = " + deltaPlim2
				+ " WHERE wellid = " + this.wellID;
		db.connect();
		db.execute(q);
		db.disconnect();
		this.deltaPlim2 = deltaPlim2;
	}

	public double getEvLim() {
		return evLim;
	}

	/**
	 * Sets the value of {@link evLim}, will write the new value to the
	 * {@link Database}.
	 * 
	 * @param double - new evLim
	 */
	public void setEvLim(double evLim) {
		db.connect();
		String q = "UPDATE welldata SET evlimit = " + evLim
				+ " WHERE wellid = " + this.wellID;
		db.execute(q);
		db.disconnect();
		this.evLim = evLim;
	}

	public double getCritP() {
		return critP;
	}

	/**
	 * Sets the value of {@link critP}, will write the new value to the
	 * {@link Database}.
	 * 
	 * @param double - new critP
	 */
	public void setCritP(double critP) {
		if (getCritP() == critP) {
			return;
		}
		db.connect();
		String q = "UPDATE welldata SET critp = " + critP + " WHERE wellid = "
				+ this.wellID;
		db.execute(q);
		db.disconnect();
		this.critP = critP;
	}

	public double getCritR() {
		return critR;
	}

	/**
	 * Sets the value of {@link critR}, will write the new value to the
	 * {@link Database}.
	 * 
	 * @param double - new critR
	 */
	public void setCritR(double critR) {
		if (getCritR() == critR) {
			return;
		}
		db.connect();
		String q = "UPDATE welldata SET critr = " + critR + " WHERE wellid = "
				+ this.wellID;
		db.execute(q);
		db.disconnect();
		this.critR = critR;
	}

	/**
	 * Gets the {@link Platform} this WatchWell is located on
	 * 
	 * @return {@link Platform}
	 */
	public Platform getPlatform() {
		return this.platform;
	}

	public double getCritT() {
		return this.tLim;
	}

	/**
	 * Sets the value of {@link tLim}, will write the new value to the
	 * {@link Database}.
	 * 
	 * @param double - new tLim
	 */
	public void setCritT(double tLim) {
		if (getCritT() == tLim) {
			return;
		}
		db.connect();
		String q = "UPDATE welldata SET critt = " + tLim + " WHERE wellid = "
				+ this.wellID;
		db.execute(q);
		db.disconnect();
		this.tLim = tLim;
	}

	/**
	 * Refreshes the status based on the latest {@link LogEntry} in the {@link Log} attached to this object.
	 */
	public void updateStatus() {
		if(log.getLatest() == null) return;
		status = log.getLatest().getStatus();
	}

}
